﻿//-----------------------------------------------------------------------
// <copyright file="UsageController.cs" company="Redmist">
//     Copyright (c) Damien T Smith. All rights reserved.
// </copyright>
// <author>Damien Smith</author>
//-----------------------------------------------------------------------

namespace Redmist.UsageMonitor.Controller
{
    using System;
    using System.ComponentModel;
    using System.Drawing;
    using System.IO;
    using Redmist.UsageMonitor.IconCreation;
    using Redmist.UsageMonitor.Model;
    using Redmist.UsageMonitor.Properties;
    using Redmist.UsageMonitor.UsageData;
    using Redmist.UsageMonitor.View;
    using Redmist.UsageMonitor.WebIO;

    /// <summary>Coordinates the application.</summary>
    public class UsageController
    {
        /// <summary>A Redmist.UsageMonitor.View.IUsageView representing the view to be controlled.</summary>
        private IUsageView usageView;

        /// <summary>A System.ComponentModel.BackgroundWorker used for background web data retrieval.</summary>
        private BackgroundWorker usageRetrievalWorker;

        /// <summary>A Redmist.UsageMonitor.UsageData.MonthlyUsage representing retrieved usage data.</summary>
        private MonthlyUsage monthlyUsage;

        /// <summary>A Redmist.UsageMonitor.Model.GraphToolTipHandler used for managing history data graph tool tips.</summary>
        private GraphToolTipHandler graphToolTipHandler;

        /// <summary>A value indicating whether a usage data retrieval operation is currently in progress.</summary>
        private bool isRetrieving;

        /// <summary>Initializes a new instance of the UsageController class.</summary>
        /// <param name="usageView">A Redmist.UsageMonitor.View.IUsageView representing the view to be controlled.</param>
        public UsageController(IUsageView usageView)
        {
            this.usageView = usageView;
            this.InitializeController();
        }

        /// <summary>Initializes view settings, hooks up events and displays the view.</summary>
        private void InitializeController()
        {
            try
            {
                this.usageView.ApplySettings(Settings.Default);
            }
            catch
            {
                try
                {
                    Settings.Default.Reset();
                    this.usageView.ApplySettings(Settings.Default);
                }
                catch
                {
                }
            }

            this.usageView.HistoryViewModes = HistoryViewMode.CreateHistoryViewList();
            this.usageRetrievalWorker = new BackgroundWorker();
            this.graphToolTipHandler = new GraphToolTipHandler(this.usageView);
            this.usageView.ViewLoad += new EventHandler(this.UsageView_ViewLoad);
            this.usageView.ViewClosed += new EventHandler(this.UsageView_ViewClosed);
            this.usageView.UsageRequested += new EventHandler(this.UsageView_UsageRequested);
            this.usageView.IconSettingsChanged += new EventHandler(this.UsageView_IconSettingsChanged);
            this.usageRetrievalWorker.DoWork += new DoWorkEventHandler(this.UsageFetcher_DoWork);
            this.usageView.UsageParametersUpdated += new EventHandler(this.ViewUsage_GraphParametersUpdated);
            this.usageRetrievalWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(this.UsageFetcher_RunWorkerCompleted);
            this.usageView.UpdateIntervalChanged += new EventHandler(this.UsageView_UpdateIntervalChanged);
            if (Settings.Default.RememberLogin)
            {
                this.usageView.StartTimer();
            }
        }

        /// <summary>Retrieves usage data.</summary>
        /// <param name="sender">A System.Object representing the sending object.</param>
        /// <param name="e">A System.EventArgs representing the event arguments.</param>
        private void UsageView_UsageRequested(object sender, EventArgs e)
        {
            this.RetrieveUsage();
        }

        /// <summary>Sets up the usage timer.</summary>
        /// <param name="sender">A System.Object representing the sending object.</param>
        /// <param name="e">A System.EventArgs representing the event arguments.</param>
        private void UsageView_UpdateIntervalChanged(object sender, EventArgs e)
        {
            this.SetUsageTimer();
        }

        /// <summary>Initializes the timer for the view based on the view update interval.</summary>
        private void SetUsageTimer()
        {
            this.usageView.TimerIntervalMilliseconds = this.usageView.UpdateIntervalMinutes * 60000;
            this.usageView.StartTimer();
        }

        /// <summary>Saves the current view settings and updates the tray icon.</summary>
        /// <param name="sender">A System.Object representing the sending object.</param>
        /// <param name="e">A System.EventArgs representing the event arguments.</param>
        private void UsageView_IconSettingsChanged(object sender, EventArgs e)
        {
            this.SaveViewSettings();
            this.UpdateNotifyIcon(this.usageView.Model);
        }

        /// <summary>Saves the current view settings.</summary>
        /// <param name="sender">A System.Object representing the sending object.</param>
        /// <param name="e">A System.EventArgs representing the event arguments.</param>
        private void UsageView_ViewClosed(object sender, EventArgs e)
        {
            this.SaveViewSettings();
        }

        /// <summary>Sets the view location.</summary>
        /// <param name="sender">A System.Object representing the sending object.</param>
        /// <param name="e">A System.EventArgs representing the event arguments.</param>
        /// <remarks>The form location can only be reliably restored after it has been loaded.</remarks>
        private void UsageView_ViewLoad(object sender, EventArgs e)
        {
            this.usageView.SetLocation(Settings.Default.FormLocation);
        }

        /// <summary>Processes the result of the background worker usage retrieval.</summary>
        /// <param name="sender">A System.Object representing the sending object.</param>
        /// <param name="e">A System.ComponentModel.RunWorkerCompletedEventArgs representing the return results from the background worker.</param>
        private void UsageFetcher_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            UsageRetrievalResult usageRetrievalResult = (UsageRetrievalResult)e.Result;

            switch (usageRetrievalResult.UsageRetrievalStatus)
            {
                case UsageRetrievalStatus.OK:
                    try
                    {
                        this.monthlyUsage = HtmlProcessor.RetrieveMonthlyUsage(usageRetrievalResult.HtmlResult);
                        this.LogResults();
                        this.UpdateUsageModel();
                    }
                    catch
                    {
                        this.isRetrieving = false;
                        this.usageView.StopTimer();
                        this.usageView.ShowProcessingError();
                        this.SetUsageTimer();
                        return;
                    }

                    this.usageView.ShowTimeRetrieved();
                    this.SetUsageTimer();
                    break;
                case UsageRetrievalStatus.Timeout:
                    this.usageView.ShowTimeoutError();
                    this.SetUsageTimer();
                    break;
                case UsageRetrievalStatus.ConnectionError:
                    this.usageView.ShowConnectionError();
                    this.SetUsageTimer();
                    break;
                case UsageRetrievalStatus.AuthenticationError:
                    this.usageView.ShowAuthenticationError();
                    this.usageView.StopTimer();
                    break;
                default:
                    break;
            }

            this.isRetrieving = false;
        }

        /// <summary>Logs the current monthly usage data to a log file.</summary>
        private void LogResults()
        {
            if (!this.usageView.IsLoggingEnabled)
            {
                return;
            }

            try
            {
                using (StreamWriter logFile = UsageLogger.InitializeLogFileWriter())
                {
                    UsageLogger.LogToFile(logFile, this.monthlyUsage);
                }
            }
            catch (Exception ex)
            {
                this.usageView.ShowLoggingError(ex.Message);
                throw;
            }
        }

        /// <summary>Generates a usage model based on the current monthly usage data and view settings.</summary>
        private void UpdateUsageModel()
        {
            if (this.monthlyUsage == null)
            {
                return;
            }

            IUsageModel usageModel = ModelFactory.GenerateUsageModel(
                this.monthlyUsage,
                this.usageView.CurrentPlanType,
                this.usageView.CombinedUsage,
                this.usageView.ReducedOffPeakRatio,
                this.usageView.FixedOffPeakQuotaMegabytes,
                this.usageView.IsGraphCountedOffPeak,
                this.usageView.IsSharedGraphScale,
                this.usageView.SelectedHistoryMode,
                this.usageView.GraphHistoryDays);

            this.usageView.Model = usageModel;
            this.UpdateNotifyIcon(usageModel);
        }

        /// <summary>Updates the view's tray icon based on the supplied usage model.</summary>
        /// <param name="usageModel">A Redmist.UsageMonitor.Model.IUsageModel representing a usage model.</param>
        private void UpdateNotifyIcon(IUsageModel usageModel)
        {
            if (usageModel == null)
            {
                return;
            }

            int peakRemainingPercentage = (int)((double)usageModel.PeakRemaining / usageModel.PeakRemainingProgressMaximum * 100);
            int offPeakRemainingPercentage = (int)((double)usageModel.OffPeakRemaining / usageModel.OffPeakRemainingProgressMaximum * 100);
            Color statusColor;
            int percent;
            if (peakRemainingPercentage > offPeakRemainingPercentage)
            {
                statusColor = TrayIconCreator.TranslateStatusColor(usageModel.OffPeakRemainingColor);
                percent = offPeakRemainingPercentage;
            }
            else
            {
                statusColor = TrayIconCreator.TranslateStatusColor(usageModel.PeakRemainingColor);
                percent = peakRemainingPercentage;
            }

            int iconWidth = OSInfo.RetrieveNotifyIconWidth(this.usageView.CreateGraphics());
            Bitmap bitmap = new Bitmap(iconWidth, iconWidth);
            Settings settings = Settings.Default;
            TrayIconCreator iconCreator = new TrayIconCreator(
                bitmap, 
                percent,
                statusColor,
                settings.IconBackColor,
                settings.IconTextColor,
                settings.IconTextShadowColor,
                settings.IconTextClearTypeEnabled,
                settings.IconBackColorEnabled,
                settings.IconTextShadowEnabled);

            this.usageView.TrayIcon = iconCreator.CreateIcon();
        }

        /// <summary>Sets up the background worker to retrieve HTML usage data using the view's current phone number/password.</summary>
        /// <param name="sender">A System.Object representing the sending object.</param>
        /// <param name="e">A System.ComponentModel.DoWorkEventArgs representing the arguments for the background worker.</param>
        private void UsageFetcher_DoWork(object sender, DoWorkEventArgs e)
        {
            e.Result = HtmlRetriever.RetrieveUsageHtmlPage(this.usageView.PhoneNumber, this.usageView.Password);
        }

        /// <summary>Starts retrieving usage data with the background worker.</summary>
        private void RetrieveUsage()
        {
            if (this.isRetrieving)
            {
                return;
            }

            this.isRetrieving = true;
            this.usageView.ShowRetrievalInProgressMessage();
            this.usageRetrievalWorker.RunWorkerAsync();
        }

        /// <summary>Updates the usage model.</summary>
        /// <param name="sender">A System.Object representing the sending object.</param>
        /// <param name="e">A System.EventArgs representing the event arguments.</param>
        private void ViewUsage_GraphParametersUpdated(object sender, EventArgs e)
        {
            this.UpdateUsageModel();
        }

        /// <summary>Retrieves the current settings from the view and saves them to the application's default settings.</summary>
        private void SaveViewSettings()
        {
            this.usageView.RetrieveSettings(Settings.Default);
            Settings.Default.Save();
        }
    }
}
